/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - core/math/matrix
// Copyright (c) 2014.  All Rights Reserved
//
// File:		AEMatrix4f.cpp
// Author:		Gianluca Belardelli
// Date:		15/12/2014
//
/////////////////////////////////////////////////////////////////////////
#include <AECore.h>

void AEMatrix4f::SetPerspectiveProjectionMatrix( float fViewWidth, float fViewHeight, float fNearZ, float fFarZ, AEClipSpaceDepthRange::Enum DepthRange, AEClipSpaceYRange::Enum YRange )
{
}

void AEMatrix4f::SetPerspectiveProjectionMatrix( float fLeft, float fRight, float fBottom, float fTop, float fNearZ, float fFarZ, AEClipSpaceDepthRange::Enum DepthRange, AEClipSpaceYRange::Enum YRange )
{
}

void AEMatrix4f::SetPerspectiveProjectionMatrixFromFovY( float fFieldOfViewY, float fAspectRatioWidthDivHeight, float fNearZ, float fFarZ, AEClipSpaceDepthRange::Enum DepthRange, AEClipSpaceYRange::Enum YRange )
{
	AEFLOAT32 fYScale = AEMath::Tan( fFieldOfViewY / 2.0f );
	AEFLOAT32 fXScale = fYScale / fAspectRatioWidthDivHeight;
	
	SetZero();
	SetElement<0,0>( 1.0f / ( fAspectRatioWidthDivHeight * fYScale ) );
	SetElement<1,1>( 1.0f / fYScale );
	SetElement<2,2>( - ( fFarZ + fNearZ ) / ( fFarZ - fNearZ ) );
	SetElement<3,2>( - (2.0f * fFarZ * fNearZ ) / ( fFarZ - fNearZ ) );
	SetElement<2,3>( -1.0f );
}

void AEMatrix4f::SetPerspectiveProjectionMatrixFromFovX( float fFieldOfViewX, float fAspectRatioWidthDivHeight, float fNearZ, float fFarZ, AEClipSpaceDepthRange::Enum DepthRange , AEClipSpaceYRange::Enum YRange )
{
	AEFLOAT32 fYScale = AEMath::Tan( fFieldOfViewX / 2.0f );
	AEFLOAT32 fXScale = fYScale / fAspectRatioWidthDivHeight;
	
	SetZero();
	SetElement<0,0>( 1.0f / ( fAspectRatioWidthDivHeight * fYScale ) );
	SetElement<1,1>( 1.0f / fYScale );
	SetElement<2,2>( - ( fFarZ + fNearZ ) / ( fFarZ - fNearZ ) );
	SetElement<3,2>( - (2.0f * fFarZ * fNearZ ) / ( fFarZ - fNearZ ) );
	SetElement<2,3>( -1.0f );
}
/*
void AEMatrix4f::SetLookAtMatrix( AEVector4fRefParam vcStartPos, AEVector4fRefParam vcTargetPos, AEVector4fRefParam vcUpDir )
{
	AEVector4f vcXAxis, vcYAxis, vcZAxis;
	vcZAxis.SetSub( vcTargetPos, vcStartPos );	// f
	//vcZAxis.SetSub( vcStartPos, vcTargetPos );	// f
	vcZAxis.Normalize<4, AEMathAccuracyMode::AE_ACC_FULL, AE_SQRT_SET_ZERO>();

	vcXAxis.SetCross( vcUpDir, vcZAxis );	// s
	vcXAxis.Normalize<4, AEMathAccuracyMode::AE_ACC_FULL, AE_SQRT_SET_ZERO>();

	vcYAxis.SetCross( vcZAxis, vcXAxis ); // u
	
	// Matrice di LookAt
	// [ vcXAxis.x,                 vcYAxis.x,              -vcZAxis.x,               0]
	// [ vcXAxis.y,                 vcYAxis.y,              -vcZAxis.y,               0]
	// [ vcXAxis.z,                 vcYAxis.z,              -vcZAxis.z,               0]
	// [ -vcXAxis.dot(vcStartPos), -vcYAxis.dot(vcStartPos), vcZAxis.dot(vcStartPos), 1]

	SetIdentity();
	SetColumn<0>( vcXAxis );
	SetColumn<1>( vcYAxis );

	SetElement<0,2>( -vcZAxis.GetComponent<0>() );
	SetElement<1,2>( -vcZAxis.GetComponent<1>() );
	SetElement<2,2>( -vcZAxis.GetComponent<2>() );

	SetElement<3,0>( -(vcXAxis.GetDot<3>( vcStartPos ) ) );
	SetElement<3,1>( -(vcYAxis.GetDot<3>( vcStartPos ) ) );
	SetElement<3,2>( (vcZAxis.GetDot<3>( vcStartPos ) ) );
	

	//SetElement<3,3>( 0.0 );
	//SetColumn<1>( vcYAxis );
	//SetElement<3,1>( -(vcYAxis.Dot4xyz1(vcStartPos)) );
	//SetColumn<2>( vcZAxis );
	//SetElement<3,2>( (vcZAxis.Dot4xyz1(vcStartPos)) );
	//SetColumn<3>( AEVector4f::GetConstant<AE_QUADREAL_0001>() );
}
*/
void AEMatrix4f::SetLookAtMatrix( AEVector4fRefParam vcStartPos, AEVector4fRefParam vcTargetPos, AEVector4fRefParam vcUpDir )
{
	AEVector4f vcXAxis, vcYAxis, vcZAxis;
	vcZAxis.SetSub( vcStartPos, vcTargetPos );	// f
	vcZAxis.Normalize<4, AE_ACC_FULL, AE_SQRT_SET_ZERO>();

	vcXAxis.SetCross( vcUpDir, vcZAxis );	// s
	vcXAxis.Normalize<4, AE_ACC_FULL, AE_SQRT_SET_ZERO>();

	vcYAxis.SetCross( vcZAxis, vcXAxis ); // u
	
	// Yaw = x
	// Pitch = y
	/*float fPitch = AEMath::Degrees2Radians( 0.0f );
	float fYaw = AEMath::Degrees2Radians( 30.0f );
	float fRoll = AEMath::Degrees2Radians( 30.0f );

	float cosPitch = cos(fPitch);
    float sinPitch = sin(fPitch);
    float cosYaw = cos(fYaw);
    float sinYaw = sin(fYaw);
	float cosRoll = cos(fRoll);
	float sinRoll = sin(fRoll);

	// Matrice di rotazione sull'asse X
	// [ 1,         0,          0, 0]
	// [ 0, cos(xrot),  sin(xrot), 0]
	// [ 0, -sin(xrot), cos(xrot), 0]
	// [ 0,         0,          0, 1]

	// Matrice di rotazione sull'asse Y
	// [ cos(yrot), 0, -sin(yrot), 0]
	// [         0, 1,          0, 0]
	// [ sin(yrot), 0,  cos(yrot), 0]
	// [         0, 0,          0, 1]

	vcXAxis.Set( cosYaw*cosPitch, (cosYaw*sinPitch*sinRoll) + (sinYaw*cosRoll), (-cosYaw*sinPitch*cosRoll) + (sinYaw*sinRoll), 0 );
	vcYAxis.Set( -sinYaw * cosPitch, (-sinYaw*sinPitch*sinRoll) + (cosYaw*cosRoll), (sinYaw*sinPitch*cosRoll) + (cosYaw*sinRoll), 0 );
	vcZAxis.Set( sinPitch, -cosPitch*sinRoll, cosPitch * cosRoll, 0 );*/

	// Matrice di LookAt
	// [ vcXAxis.x,                 vcYAxis.x,                vcZAxis.x,               0]
	// [ vcXAxis.y,                 vcYAxis.y,                vcZAxis.y,               0]
	// [ vcXAxis.z,                 vcYAxis.z,                vcZAxis.z,               0]
	// [ -vcXAxis.dot(vcStartPos), -vcYAxis.dot(vcStartPos), -vcZAxis.dot(vcStartPos), 1]

	SetIdentity();
	SetColumn<0>( vcXAxis );
	SetColumn<1>( vcYAxis );
	SetColumn<2>( vcZAxis );

	SetElement<3,0>( -(vcXAxis.GetDot<3>( vcStartPos ) ) );
	SetElement<3,1>( -(vcYAxis.GetDot<3>( vcStartPos ) ) );
	SetElement<3,2>( -(vcZAxis.GetDot<3>( vcStartPos ) ) );
}

void AEMatrix4f::SetOrthographicProjectionMatrix( float fViewWidth, float fViewHeight, float fNearZ, float fFarZ, AEClipSpaceDepthRange::Enum DepthRange, AEClipSpaceYRange::Enum YRange )
{
	m_vcColumn0.Set( 2.0f/fViewWidth, 0.0f, 0.0f, 0.0f );
	m_vcColumn1.Set( 0.0f, 2.0f/fViewHeight, 0.0f, 0.0f );
	m_vcColumn2.Set( 0.0f, 0.0f, 2.0f/(fFarZ-fNearZ), 0.0f );
	m_vcColumn3.Set( -1.0f, 
					-1.0f,
					( (fFarZ+fNearZ)/(fFarZ-fNearZ) ),
					1.0f );

	m_vcColumn0.Set( 2.0f/fViewWidth, 0.0f, 0.0f, 0.0f );
	m_vcColumn1.Set( 0.0f, 2.0f/fViewHeight, 0.0f, 0.0f );
	m_vcColumn2.Set( 0.0f, 0.0f, 2.0f/(fFarZ-fNearZ), 0.0f );
	m_vcColumn3.Set( -1.0f, 
					-1.0f,
					( (fFarZ+fNearZ)/(fNearZ-fFarZ) ),
					1.0f );

	//SetOrthographicProjectionMatrix( 0, fViewWidth, 0, fViewHeight, fNearZ, fFarZ );
	SetOrthographicProjectionMatrix( 0, fViewWidth, fViewHeight, 0, fNearZ, fFarZ );
}

void AEMatrix4f::SetOrthographicProjectionMatrix( float fLeft, float fRight, float fBottom, float fTop, float fNearZ, float fFarZ, AEClipSpaceDepthRange::Enum DepthRange, AEClipSpaceYRange::Enum )
{
	SetIdentity();

	m_vcColumn0.Set( 2.0f/(fRight-fLeft), 0.0f, 0.0f, -( (fRight+fLeft)/(fRight-fLeft) ) );
	m_vcColumn1.Set( 0.0f, 2.0f/(fTop-fBottom), 0.0f, -( (fTop+fBottom)/(fTop-fBottom) ) );
	m_vcColumn2.Set( 0.0f, 0.0f, 2.0f/(fFarZ-fNearZ), ( (fFarZ+fNearZ)/(fFarZ-fNearZ) ) );
	
	/*m_vcColumn0.Set( 2.0f/(fRight-fLeft), 0.0f, 0.0f, 0.0f );
	m_vcColumn1.Set( 0.0f, -2.0f/(fTop-fBottom), 0.0f, 0.0f );
	m_vcColumn2.Set( 0.0f, 0.0f, 2.0f/(fFarZ-fNearZ), 0);
	m_vcColumn3.Set( -1.0f, 1.0f, ( (fFarZ+fNearZ)/(fNearZ-fFarZ) ), 1.0f );*/

	/*m_vcColumn3.Set( -( (fRight+fLeft)/(fRight-fLeft) ), 
					-( (fTop+fBottom)/(fTop-fBottom) ),
					( (fFarZ+fNearZ)/(fFarZ-fNearZ) ),
					1.0f );*/
}

void AEMatrix4f::SetRotationMatrixX( float fDegree )
{
	// Matrice di rotazione sull'asse X
	// [ 1,         0,          0, 0]
	// [ 0, cos(xrot),  sin(xrot), 0]
	// [ 0, -sin(xrot), cos(xrot), 0]
	// [ 0,         0,          0, 1]
	const AEFLOAT32 fTheta = AEMath::Degrees2Radians( fDegree );
	const AEFLOAT32 fCos = AEMath::Cos( fTheta );
	const AEFLOAT32 fSin = AEMath::Sin( fTheta );

	m_vcColumn0.Set( 1.0, 0.0, 0.0 , 0.0 );
	m_vcColumn1.Set( 0.0, fCos, fSin, 0.0 );
	m_vcColumn2.Set( 0.0, -fSin, fCos, 0.0 );
	m_vcColumn3.Set( 0.0, 0.0, 0.0, 1.0 );
}

void AEMatrix4f::SetRotationMatrixY( float fDegree )
{
	// Matrice di rotazione sull'asse Y
	// [ cos(yrot), 0, -sin(yrot), 0]
	// [         0, 1,          0, 0]
	// [ sin(yrot), 0,  cos(yrot), 0]
	// [         0, 0,          0, 1]

	const AEFLOAT32 fTheta = AEMath::Degrees2Radians( fDegree );
	const AEFLOAT32 fCos = AEMath::Cos( fTheta );
	const AEFLOAT32 fSin = AEMath::Sin( fTheta );

	m_vcColumn0.Set( fCos, 0.0, -fSin , 0.0 );
	m_vcColumn1.Set( 0.0, 1.0, 0.0, 0.0 );
	m_vcColumn2.Set( fSin, 0.0, fCos, 0.0 );
	m_vcColumn3.Set( 0.0, 0.0, 0.0, 1.0 );
}

void AEMatrix4f::SetRotationMatrixZ( float fDegree )
{
	// Matrice di rotazione sull'asse Z
	// [ cos(zrot), -sin(zrot), 0, 0]
	// [ sin(zrot),  cos(zrot), 0, 0]
	// [         0,          0, 1, 0]
	// [         0,          0, 0, 1]
	const AEFLOAT32 fTheta = AEMath::Degrees2Radians( fDegree );
	const AEFLOAT32 fCos = AEMath::Cos( fTheta );
	const AEFLOAT32 fSin = AEMath::Sin( fTheta );

	m_vcColumn0.Set( fCos, fSin, 0.0, 0.0 );
	m_vcColumn1.Set( -fSin, fCos, 0.0, 0.0 );
	m_vcColumn2.Set( 0.0, 0.0, 1.0, 0.0 );
	m_vcColumn3.Set( 0.0, 0.0, 0.0, 1.0 );
}
void AEMatrix4f::SetTranslationMatrix( AEVector4fRefParam vcTranslation )
{
	// Matrice di traslazione
	// [ 1,  0,  0, 0]
	// [ 0,  1,  0, 0]
	// [ 0,  0,  1, 0]
	// [tx, ty, tz, 1]

	SetIdentity();
	/*m_vcColumn0.SetComponent<3>( vcTranslation.GetComponent<0>() );
	m_vcColumn1.SetComponent<3>( vcTranslation.GetComponent<1>() );
	m_vcColumn2.SetComponent<3>( vcTranslation.GetComponent<2>() );
	m_vcColumn3.SetComponent<3>( vcTranslation.GetComponent<3>() );*/
	SetRow<3>( vcTranslation );
}

void AEMatrix4f::SetScalingMatrix( const AEVector4f &vcScaleXYZ )
{
	// Matrice di scalatura
	// sx, sy and sz represent our scale values.
	// [ sx,  0,  0, 0]
	// [  0, sy,  0, 0]
	// [  0,  0, sz, 0]
	// [  0,  0,  0, 1]
	SetDiagonal( vcScaleXYZ );
}
